﻿#include "eu2_demo.h"
#include <iostream>
using namespace CameraAI;

void CameraHelper::InitUsbCamLib()
{
	HRESULT hr = CoInitialize(NULL);
	if (S_OK == hr)
	{
		printf("Com init success!\n");
	}
	else if (S_FALSE == hr)
	{
		printf("Com has init!\n");
	}
	else {
		printf("com init fail!\n");
	}
}
void CameraHelper::UnInitUsbCamLib()
{
	CoUninitialize();
}

int CameraHelper::openCameraDevice(const char* devPath)
{

	HRESULT hr;
	ICreateDevEnum* piCreateDevEnum;
	IEnumMoniker* pEnumCat = NULL;
	IMoniker* pMoniker = NULL;

	hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
		IID_ICreateDevEnum, (void**)&piCreateDevEnum);
	if (SUCCEEDED(hr)) {
		hr = piCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0);
		if (hr == S_OK) {
			ULONG monikerFetched;
			while (pEnumCat->Next(1, &pMoniker, &monikerFetched) == S_OK) {  //pMoniker每次都变
				IPropertyBag* pPropBag;
				VARIANT var;
				hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropBag);
				if (FAILED(hr)) {
					pMoniker->Release();
					continue;
				}
				VariantInit(&var);
				hr = pPropBag->Read(L"DevicePath", &var, 0);
				if (SUCCEEDED(hr)) {
					char* path = _com_util::ConvertBSTRToString(var.bstrVal);
					/*printf("---> %s\n", path);
					printf("===> %s\n", devPath);*/
					if (strncmp(path, devPath, strlen(devPath)) == 0) {
						hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)(&piFilter));
						if (SUCCEEDED(hr)) {
							SysFreeString(var.bstrVal);
							pPropBag->Release();
							pMoniker->Release();
							return 0;
						}
						else {
							/*printf("BindToObject failed. %x", hr);*/
						}
					}
					SysFreeString(var.bstrVal);
				}

				pPropBag->Release();
				pMoniker->Release();
			}
		}
		piCreateDevEnum->Release();
	}
	else {
		/*printf("com create instance failed.");*/
	}

	return -1;
}

int CameraHelper::eu2_set(int cs, unsigned char* data, int len)
{
	ExCtrlCmd euCmd;
	int ret;

	//printf("----------->cs = %d, len = %d\n", cs, len);
	euCmd.data = data;
	euCmd.KsNodeID = 3;
	euCmd.len = len;
	euCmd.CtrlID = cs;
	euCmd.query = QUERY_SET;
	memcpy(&euCmd.guid, &xuGUID, sizeof(UsbGuid));
	ret = UvcXuCommand(&euCmd);
	return ret;
}

int CameraHelper::eu2_get(int cs, unsigned char* buf)
{
	ExCtrlCmd euCmd;

	euCmd.data = buf;
	euCmd.KsNodeID = 3;
	euCmd.len = 60;
	euCmd.CtrlID = cs;
	euCmd.query = QUERY_GET;
	memcpy(&euCmd.guid, &xuGUID, sizeof(UsbGuid));
	return UvcXuCommand(&euCmd);
}

int CameraHelper::GetIKsControl(IBaseFilter* pCaptureFilter, IKsControl** ppKsControl)
{
	HRESULT	hr;

	if (NULL == pCaptureFilter) {
		return -1;
	}

	hr = pCaptureFilter->QueryInterface(__uuidof(IKsControl),
		(void**)ppKsControl);

	if (hr != S_OK) {
		return -1;
	}

	return 0;
}

int CameraHelper::UvcXuCommand(ExCtrlCmd* cmd)
{

	IKsControl* pKsControl = NULL;
	int nNodeId;
	KSP_NODE ExtensionProp;
	HRESULT hr;
	int ret;
	if (GetIKsControl(piFilter, &pKsControl) != 0)
	{
		printf("**************err\n");
		ret = -1;
		goto end;
	}
	memcpy(&(ExtensionProp.Property.Set), &cmd->guid, sizeof(UsbGuid));
	ExtensionProp.Property.Id = cmd->CtrlID;
	ExtensionProp.NodeId = cmd->KsNodeID;
	ExtensionProp.Reserved = 0;
	if (cmd->query == QUERY_GET)
		ExtensionProp.Property.Flags = KSPROPERTY_TYPE_GET |
		KSPROPERTY_TYPE_TOPOLOGY;
	else
		ExtensionProp.Property.Flags = KSPROPERTY_TYPE_SET |
		KSPROPERTY_TYPE_TOPOLOGY;
	ULONG retLen;
	hr = pKsControl->KsProperty((PKSPROPERTY)&ExtensionProp, sizeof(ExtensionProp), reinterpret_cast<PVOID>(cmd->data),
		cmd->len, &retLen);
	if (hr != S_OK)
	{
		printf("**************errxxxxxxxxx\n");
		ret = -1;
		goto end;
	}
	ret = 0;
end:
	if (NULL != pKsControl)
	{
		pKsControl->Release();
		pKsControl = NULL;
	}
	return ret;
}

int CameraHelper::GetCurCamDevs(CamDevInfo(*devInfo)[MAX_CAM_NUM], int* hasFindDevNum)
{
	// 创建系统设备枚举器
	ICreateDevEnum* devEnum = NULL;
	auto hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&devEnum);
	if (FAILED(hr))
	{
		return -1;
	}

	// 获取视频输入设备的枚举器
	IEnumMoniker* enumMoniker = NULL;
	hr = devEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &enumMoniker, 0);
	if (FAILED(hr))
	{
		devEnum->Release();
		return -1;
	}

	// 遍历设备枚举器获取设备信息
	IMoniker* moniker = NULL;
	int devIndex = 0;
	if (enumMoniker != nullptr)
	{
		while (enumMoniker->Next(1, &moniker, NULL) == S_OK)
		{
			IPropertyBag* propertyBag = NULL;
			hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&propertyBag);
			if (SUCCEEDED(hr))
			{
				VARIANT var;
				VariantInit(&var);

				// 获取设备名称
				hr = propertyBag->Read(L"FriendlyName", &var, 0);
				if (SUCCEEDED(hr))
				{
					int size_needed = WideCharToMultiByte(CP_UTF8, 0, var.bstrVal, -1, NULL, 0, NULL, NULL);
					std::string name(size_needed, 0);
					WideCharToMultiByte(CP_UTF8, 0, var.bstrVal, -1, &name[0], size_needed, NULL, NULL);
					strncpy_s((*devInfo)[devIndex].name, name.c_str(), MAX_CAM_NAME_LEN);
					(*devInfo)[devIndex].name[MAX_CAM_NAME_LEN - 1] = '\0';  // Ensure null termination
					VariantClear(&var);
				}
				//hr = propertyBag->Read(L"CLSID", &var, 0); //{17CCA71B-ECD7-11D0-B908-00A0C9223196}
				//if (SUCCEEDED(hr))
				//{
				//	
				//	VariantClear(&var);
				//}

				hr = propertyBag->Read(L"DevicePath", &var, 0); //\\?\usb#vid_2aad&pid_6373&mi_00#7&1a6d6c9a&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global
				if (SUCCEEDED(hr))
				{
					int size_needed = WideCharToMultiByte(CP_UTF8, 0, var.bstrVal, -1, NULL, 0, NULL, NULL);
					std::string name(size_needed, 0);
					WideCharToMultiByte(CP_UTF8, 0, var.bstrVal, -1, &name[0], size_needed, NULL, NULL);
					strncpy_s((*devInfo)[devIndex].devPath, name.c_str(), MAX_CAM_NAME_LEN);
					(*devInfo)[devIndex].devPath[MAX_CAM_NAME_LEN - 1] = '\0';  // Ensure null termination
					VariantClear(&var);
				}
				//hr = propertyBag->Read(L"Description", &var, 0); //{17CCA71B-ECD7-11D0-B908-00A0C9223196}
				//if (SUCCEEDED(hr))
				//{

				//	VariantClear(&var);
				//}

				// 获取设备路径
				//hr = moniker->GetDisplayName(NULL, NULL, &var.bstrVal);
				//if (SUCCEEDED(hr))
				//{
				//	int size_needed = WideCharToMultiByte(CP_UTF8, 0, var.bstrVal, -1, NULL, 0, NULL, NULL);
				//	std::string name(size_needed, 0);
				//	WideCharToMultiByte(CP_UTF8, 0, var.bstrVal, -1, &name[0], size_needed, NULL, NULL);
				//	strncpy_s((*devInfo)[devIndex].devPath, name.c_str(), MAX_CAM_NAME_LEN);
				//	(*devInfo)[devIndex].devPath[MAX_CAM_NAME_LEN - 1] = '\0';  // Ensure null termination
				//	VariantClear(&var);
				//}
				propertyBag->Release();
			}

			moniker->Release();
			devIndex++;

			if (devIndex >= MAX_CAM_NUM)
			{
				break;
			}
		}
		enumMoniker->Release();
	}
	devEnum->Release();
	/*CoUninitialize();*/

	*hasFindDevNum = devIndex;

	return 0;
}

int CameraHelper::EnumDevice()
{
	CamDevInfo devs[MAX_CAM_NUM];
	int findNum = 0;
	auto ret = GetCurCamDevs(&devs, &findNum);
	if (ret == USB_CAM_LIB_OK)
	{
		for (size_t i = 0; i < findNum; i++)
		{
			std::cout << devs[i].devPath << std::endl;
		}
	}
	return findNum;
}
int CameraHelper::GetDeviceSatus(const char* devPath)	
{ 
	const char* image_mode_str[] = { "全景", "最佳视角", "发言人追踪", "手势追踪", "智能分屏" };
	int image_mode = 0;
	int mirror = 0;
	int lan = 0;
	auto ret = openCameraDevice(devPath);
	if (ret != 0) {
		goto end;
	}
	unsigned char buf[60];

	buf[0] = 0;
	buf[1] = 0x00;
	ret = eu2_set(0x06, buf, sizeof(buf));
	if (ret != 0) {
		goto stop;
	}
	ret = eu2_get(0x06, buf);
	if (ret != 0) {
		goto stop;
	}
	image_mode = buf[2];
	
	// mirror
	buf[0] = 0;
	buf[1] = 0x02;
	ret = eu2_set(0x05, buf, sizeof(buf));
	if (ret != 0) {
		goto stop;
	}
	ret = eu2_get(0x05, buf);
	if (ret != 0) {
		goto stop;
	}
	mirror = buf[4];
	lan = buf[6];

stop:
	piFilter->Release();
	//UnInitUsbCamLib();
	return ((lan | 0b1000) << 8) + ((mirror | 0b1000) << 4) + (image_mode | 0b1000);
end:
	return -1;
}

bool CameraHelper::SetDeviceSatus(const char* devPath, int index)
{
	bool result = false;
	const char* image_mode_str[] = { "全景", "最佳视角", "发言人追踪", "手势追踪", "智能分屏" };
	auto ret = openCameraDevice(devPath);
	if (ret != 0) {
		goto end;
	}
	unsigned char buf[60];
	 
	int image_mode;
	buf[0] = 0;
	buf[1] = 0x00;
	ret = eu2_set(0x06, buf, sizeof(buf));
	if (ret != 0) {
		goto stop;
	}
	ret = eu2_get(0x06, buf);
	if (ret != 0) {
		goto end;
	}
	image_mode = buf[2];
	image_mode = index;
	buf[0] = 1;
	buf[2] = image_mode;
	ret = eu2_set(0x06, buf, sizeof(buf));
	if (ret == 0) {
		result = true;
	}
stop:
	piFilter->Release();
end:
	return result;
}

bool CameraHelper::SetMirror(const char* devPath,bool isMirror)
{
	bool result = false;
	auto ret = openCameraDevice(devPath);
	if (ret != 0) 
	{
		goto end;
	}

	unsigned char buf[60];

	buf[0] = 0;
	buf[1] = 0x02;
	ret = eu2_set(0x05, buf, sizeof(buf));
	if (ret != 0) {
		goto stop;
	}
	ret = eu2_get(0x05, buf);
	if (ret != 0) {
		goto end;
	}
	 
	buf[0] = 1;
	buf[1] = 2;
	buf[4] = isMirror ? 1 : 0;
	ret = eu2_set(0x05, buf, sizeof(buf));
	if (ret == 0) {
		result = true;
	}
stop:
	piFilter->Release();
end:
	return result;
	
}

bool CameraHelper::SetLanguage(const char* devPath, int index)
{
	bool result = false;
	auto ret = openCameraDevice(devPath);
	if (ret != 0)
	{
		goto end;
	}

	unsigned char buf[60];

	buf[0] = 0;
	buf[1] = 0x02;
	ret = eu2_set(0x05, buf, sizeof(buf));
	if (ret != 0) {
		goto stop;
	}
	ret = eu2_get(0x05, buf);
	if (ret != 0) {
		goto end;
	}
	buf[0] = 1;
	buf[1] = 2;
	buf[2] = 0;//强行关字幕
	buf[6] = index;
	ret = eu2_set(0x05, buf, sizeof(buf));
	if (ret == 0) {
		result = true;
	}
stop:
	piFilter->Release();
end:
	return result;
}